﻿Namespace Functions

    ''' <summary>Used to Identify the kind of function.</summary>
    Public Enum MetisFunctionType
        ''' <summary>Internals are functions that map adimension, are a flattree of another funtion or that are created based on tables that are not managed by Metis (e.g. linked tables).</summary>
        Internal = 0
        ''' <summary>Input functions are used to enter data manually (in contrast do Tables/View functions where data is calculated)</summary>
        Input = 1
        ''' <summary>Table functions save their calculated output-data to a table</summary>
        Table = 2
        ''' <summary>View functions save their result as a view</summary>
        View = 3
    End Enum

    ''' <summary>Defines the actions a Table-Function can perform.</summary>
    Public Enum MetisFunctionAction
        ''' <summary>The all table data will be deleted befor the new function result is added.</summary>
        TruncateAndAdd = 0
        ''' <summary>Adds only new data from the result to the table data that does not conflict with the existing data (check by PK).</summary>
        Add = 1
        ''' <summary>Adds new data and updates any existing data where the PK already exists.</summary>
        Merge = 2
        ''' <summary>Updates only exising data where the PK alredy exists.</summary>
        Update = 3
    End Enum

    ''' <summary>Base class for all classes representing a Function</summary>
    ''' <remarks>Objects can easily created from DB data by using <see cref="MetisFunction.FunctionFactory">FunctionFactory</see> factory method.</remarks>
    Public MustInherit Class MetisFunction
        Implements IMetisEntity, IMetisNamespaceReference

        Protected _MetisCore As MetisCore

        Protected _ID As Integer = -1
        Protected _Name As String = ""
        Protected _Annotation As String = ""
        Protected _Table As DB.DBTable
        Protected WithEvents _Fields As New MetisFunctionFieldList(Me)

        Protected _BackupName As String = ""
        Protected _BackupAnnotation As String = ""
        Protected WithEvents _BackupFields As New MetisFunctionFieldList(Me)

        Protected _Namespace As MetisNamespace
        Protected _Level As Integer = 0
        Protected _UsesFunctions As MetisFunctionList
        Protected _UsedByFunctions As MetisFunctionList

        Protected _LoadState As MetisLoadState = MetisLoadState.NewDefinition
        Protected _LastUpdate As Date
        Protected _CreatedOn As Date
        Protected _ModifiedOn As Date
        Protected _IsModified As Boolean = False

        Public Event Changed(ByVal sender As IMetisEntity) Implements IMetisEntity.Changed
        Public Event Loaded(ByVal sender As IMetisEntity) Implements IMetisEntity.Loaded
        Public Event Progress(ByVal sender As IMetisEntity, ByVal Progress As MetisProgressEventArgs) Implements IMetisEntity.Progress
        Public Event Saved(ByVal sender As IMetisEntity) Implements IMetisEntity.Saved
        Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Public Shared Function FunctionFactory(ByVal MetisCore As Core.MetisCore, ByVal [Namespace] As MetisNamespace, ByVal FuncType As MetisFunctionType, _
                                               ByVal ID As Integer, ByVal Name As String, ByVal Annotation As String, _
                                               ByVal Table As DB.DBTable, ByVal ModifiedOn As Date, ByVal CreatedOn As Date, _
                                               Optional ByVal Level As Integer = 0, Optional ByVal Action As MetisFunctionAction = MetisFunctionAction.TruncateAndAdd) As MetisFunction
            Select Case FuncType
                Case MetisFunctionType.Internal
                    Return New MetisInternalFunction(MetisCore, [Namespace], ID, Name, Annotation, Table, ModifiedOn, CreatedOn)
                Case MetisFunctionType.Input
                    Return New MetisInputFunction(MetisCore, [Namespace], ID, Name, Annotation, Table, ModifiedOn, CreatedOn)
                Case MetisFunctionType.View
                    Return New MetisViewFunction(MetisCore, [Namespace], ID, Name, Annotation, Table, ModifiedOn, CreatedOn, Level)
                Case MetisFunctionType.Table
                    Return New MetisTableFunction(MetisCore, [Namespace], ID, Name, Annotation, Table, ModifiedOn, CreatedOn, Level, Action)
            End Select
        End Function

        Sub HandleFieldsChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ListChangedEventArgs) Handles _Fields.ListChanged
            If _LoadState <> MetisLoadState.Loading Then
                CheckIsModified()
                RaiseEvent Changed(Me)
            End If
        End Sub

        Public Function GetNextID() As Integer Implements IMetisCommonEntity.GetNextID
            _ID = _MetisCore.GetNextID
            RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("ID"))
            Return _ID
        End Function

        Public ReadOnly Property ID() As Integer Implements IMetisCommonEntity.ID
            Get
                Return _ID
            End Get
        End Property

        Public Property Name() As String Implements IMetisCommonEntity.Name
            Get
                Return _Name
            End Get
            Set(ByVal value As String)
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                If MetisCore.IsValidFieldName(value, 64) Then
                    Dim tmpList As MetisNamespaceList
                    If _Namespace IsNot Nothing Then
                        tmpList = _Namespace.Children
                    Else
                        tmpList = _MetisCore.Namespaces
                    End If

                    For Each item As IMetisNamespaceReference In tmpList
                        If Not Me.Equals(item) AndAlso value.Equals(item.Name, StringComparison.OrdinalIgnoreCase) AndAlso _
                           TypeOf item Is MetisFunction Then
                            Throw New Exceptions.DublicateNameException(value)
                        End If
                    Next

                    _Name = value
                    RaiseEvent Changed(Me)
                    RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Name"))
                    CheckIsModified()
                Else
                    Throw New Exceptions.InvalidNameException(value)
                End If
            End Set
        End Property

        Public ReadOnly Property FullName() As String
            Get
                If _Namespace IsNot Nothing Then
                    Return String.Format("{0}.{1}", _Namespace.ToString, _Name)
                Else
                    Return _Name
                End If
            End Get
        End Property

        Public Property Annotation() As String Implements IMetisCommonEntity.Annotation
            Get
                Return _Annotation
            End Get
            Set(ByVal value As String)
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                _Annotation = value
                RaiseEvent Changed(Me)
                RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Annotation"))
                CheckIsModified()
            End Set
        End Property

        Public ReadOnly Property IsModified() As Boolean Implements IMetisEntity.IsModified
            Get
                Return _IsModified
            End Get
        End Property

        Public ReadOnly Property LoadState() As MetisLoadState Implements IMetisEntity.LoadState
            Get
                Return _LoadState
            End Get
        End Property

        Public ReadOnly Property LastUpdate() As Date Implements IMetisEntity.LastUpdate
            Get
                Return _LastUpdate
            End Get
        End Property

        Public ReadOnly Property CreatedOn() As Date Implements IMetisEntity.CreatedOn
            Get
                Return _CreatedOn
            End Get
        End Property


        Public ReadOnly Property ModifiedOn() As Date Implements IMetisEntity.ModifiedOn
            Get
                Return _ModifiedOn
            End Get
        End Property

        Public ReadOnly Property Fields() As MetisFunctionFieldList
            Get
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                Return _Fields
            End Get
        End Property

        Public Property [Namespace]() As MetisNamespace Implements IMetisNamespaceReference.Namespace
            Get
                Return _Namespace
            End Get
            Set(ByVal value As MetisNamespace)

                Dim tmpList As MetisNamespaceList = If(value IsNot Nothing, value.Children, _MetisCore.Namespaces)

                For Each item As IMetisNamespaceReference In tmpList
                    If Not Me.Equals(item) AndAlso Me.Name.Equals(item.Name, StringComparison.OrdinalIgnoreCase) AndAlso _
                       TypeOf item Is MetisFunction Then
                        Throw New Exceptions.DublicateNameException(Me.Name)
                    End If
                Next

                _Namespace = value

                If _LoadState <> MetisLoadState.NewDefinition Then
                    Dim tmpSetValue As String = If(_Namespace IsNot Nothing, _Namespace.ID.ToString, "NULL")

                    Dim tmpStr As String = String.Format("UPDATE dam_functions SET namespace_fk={1} " & _
                                                         "WHERE function_id={0}", _ID, tmpSetValue)

                    Dim OraCmd As New OracleCommand(tmpStr, _MetisCore.Connection)
                    OraCmd.ExecuteNonQuery()
                End If


                RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Name"))

            End Set
        End Property

        Public Overridable Property Table() As DB.DBTable
            Get
                Return _Table
            End Get
            Set(ByVal value As DB.DBTable)
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                _Table = value
                RaiseEvent Changed(Me)
                RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs("Table"))
                CheckIsModified()
            End Set
        End Property

        Public ReadOnly Property Level() As Integer
            Get
                Return _Level
            End Get
        End Property

        Public ReadOnly Property Uses() As MetisFunctionList
            Get
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                Return _UsesFunctions
            End Get
        End Property

        Public ReadOnly Property UsedBy() As MetisFunctionList
            Get
                If _LoadState = MetisLoadState.NotLoaded Then Reload()
                Return _UsedByFunctions
            End Get
        End Property

        Public ReadOnly Property MetisCore() As Core.MetisCore
            Get
                Return _MetisCore
            End Get
        End Property

        Public Overrides Function Equals(ByVal obj As Object) As Boolean
            If TypeOf obj Is MetisFunction AndAlso DirectCast(obj, MetisFunction).ID = _ID Then
                Return True
            Else
                Return MyBase.Equals(obj)
            End If
        End Function

        Public Function CompareTo(ByVal other As IMetisCommonEntity) As Integer Implements System.IComparable(Of IMetisCommonEntity).CompareTo
            Return String.Compare(_Name, other.Name, True)
        End Function

        Public Sub SaveFormat()
            If _LoadState <> MetisLoadState.NewDefinition Then

                Dim OraCmd As OracleCommand
                Dim sSetString As String
                Dim SQLscript As String = ""

                For Each item As MetisFunctionField In _Fields
                    Dim OldField As MetisFunctionField = _BackupFields.FieldByID(item.ID)
                    If OldField IsNot Nothing Then
                        sSetString = ""

                        If item.NumberFormat <> OldField.NumberFormat Then sSetString &= ",number_format='" & item.NumberFormat & "'"
                        If item.ColumnWidth <> OldField.ColumnWidth Then sSetString &= ",column_width=" & item.ColumnWidth

                        If sSetString <> "" Then SQLscript &= String.Format("UPDATE dam_function_fields SET {1} " & _
                                                                            "WHERE function_field_id={0};", item.ID, sSetString.Substring(1)) & vbLf
                    End If
                Next

                If SQLscript <> "" Then
                    OraCmd = New OracleCommand(String.Format("--- Save Format {1} --- {2}BEGIN {0} COMMIT; END;{2}", SQLscript, _Name, vbCrLf), _MetisCore.Connection)
                    OraCmd.ExecuteNonQuery()
                End If

            End If
        End Sub


        Public MustOverride ReadOnly Property FunctionType() As MetisFunctionType

        Public MustOverride Sub Reload() Implements IMetisEntity.Reload

        Public MustOverride Sub Save() Implements IMetisEntity.Save

        Public MustOverride Sub CheckIsModified()


    End Class
End Namespace

